package async;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import util.UnsafeUtil;

public class AsyncFuture<T> extends CompletableFuture<T> {
	private static final long resultOffset = UnsafeUtil.objectFieldOffset(CompletableFuture.class, "result");
	private Node<T> tail;

	private static class Node<T> {
		final Function<? super T, ? extends CompletionStage<T>> fn;
		Node<T> next;

		Node(Function<? super T, ? extends CompletionStage<T>> f) {
			fn = f;
		}
	}

	public static <T> AsyncFuture<T> completed(T value) {
		AsyncFuture<T> af = new AsyncFuture<>();
		af.complete(value);
		return af;
	}

	@SuppressWarnings("removal")
	public AsyncFuture<T> yield() {
		UnsafeUtil.unsafe.putObject(this, resultOffset, null);
		return this;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
		Node<T> n = new Node<>((Function<? super T, ? extends CompletionStage<T>>)fn);
		Node<T> t = tail;
		if (t != null) {
			n.next = t.next;
			t.next = n;
		} else
			n.next = n;
		tail = n;
		return (CompletableFuture<U>)this;
	}

	@SuppressWarnings("removal")
	@Override
	public boolean complete(T value) {
		UnsafeUtil.unsafe.putObject(this, resultOffset, value);
		Node<T> t = tail;
		if (t != null) {
			Node<T> n = t.next;
			t.next = null;
			while (n != null) {
				tail = null;
				//noinspection RedundantCast
				CompletableFuture<T> cf = ((CompletionStage<T>)n.fn.apply(value)).toCompletableFuture();
				n = n.next;
				UnsafeUtil.unsafe.putObject(this, resultOffset, value = cf.getNow(null));
				if (value == null) {
					Node<T> tt = tail;
					if (tt != null) {
						if (n != null) {
							Node<T> h = tt.next;
							tt.next = n;
							t.next = h;
							tail = t;
						}
						break;
					}
					if (n != null) {
						t.next = n;
						tail = t;
					}
					break;
				}
			}
		}
		return true;
	}

	@SuppressWarnings("removal")
	@Override
	public boolean isDone() {
		return UnsafeUtil.unsafe.getObject(this, resultOffset) != null;
	}

	@SuppressWarnings({"unchecked", "removal"})
	@Override
	public T getNow(T valueIfAbsent) {
		Object obj = UnsafeUtil.unsafe.getObject(this, resultOffset);
		return (obj != null) ? (T)obj : valueIfAbsent;
	}

	@SuppressWarnings({"unchecked", "removal"})
	@Override
	public T join() {
		return (T)UnsafeUtil.unsafe.getObject(this, resultOffset);
	}

	@Override
	public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn) {
		return this;
	}
}
